#pragma once

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctime>
#include <string>
#include <vector>
#include <iostream>
#include "task.hpp"

const int process_count = 10;

struct channel
{
    channel(int fd, pid_t SlaveId, int masterId, std::string& SlaveName)
        :_fd(fd), _SlaveId(SlaveId), _masterId(masterId), _SlaveName(SlaveName)
    { }

    int _fd;
    pid_t _SlaveId;
    pid_t _masterId;
    std::string _SlaveName;
};

class ProcessPool
{
public:
    static ProcessPool* getProcessPool()
    {
        if(_singletonProcessPool == nullptr) {
            _singletonProcessPool = new ProcessPool;
        }
        return _singletonProcessPool;
    }

    void TaskAllocation(int taskNo)
    {
        //考虑子进程的负载均衡：随机channel
        int ChannelNo = rand() % process_count;
        channel slaveChannel = _process_pool[ChannelNo];
        int no = taskNo;
        write(slaveChannel._fd, &no, sizeof(taskNo));
        std::cout << "Father allocate a task complete -- taskNo:" << taskNo << std::endl;
    }

    void Destroy()
    {
        std::cout << "ProcessPool Destroy" << std::endl;
        this->~ProcessPool();
    }

    ~ProcessPool()
    {
        for(int i = 0; i < process_count; ++i) {
            close(_process_pool[i]._fd);
            std::cout << "channel fd closed -- fd:" << _process_pool[i]._fd << std::endl;
            waitpid(_process_pool[i]._SlaveId, nullptr, 0);
            std::cout << "child clean -- pid:" << _process_pool[i]._SlaveId << std::endl;
        }
        //sleep(5);
    }

private:
    void ChildDoTask()
    {
        int taskNo = 0;
        while(true)
        {
            ssize_t s = read(0, &taskNo, sizeof(taskNo));
            if(s > 0)
            {
                std::cout << "Child get a task -- taskNo:" << taskNo << std::endl;
                taskList[taskNo]();
            }
            else if(s == 0) { break; }
            else { perror("ChildDoTask:read err"); exit(1); }
        }
    }

private:
    std::vector<channel> _process_pool;

private:
    static ProcessPool* _singletonProcessPool;

    ProcessPool(const ProcessPool& p_p) = delete;
    ProcessPool& operator=(const ProcessPool& p_p) = delete;

    ProcessPool()
    {
        srand(time(nullptr));
        std::vector<int> oldfds;
        for(int i = 0; i < process_count; ++i)
        {
            int pipefd[2];
            int ret = pipe(pipefd); if(ret < 0) { perror("InitProcessPool:pipe err"); exit(1); }
            int id = fork();
            if(id < 0) { perror("InitProcessPool:fork err"); exit(1); }
            else if(id == 0)
            {
                for(auto fd : oldfds) { close(fd); }
                close(pipefd[1]);
                dup2(pipefd[0], 0);
                ChildDoTask();
                exit(0);
            }
            else
            {
                close(pipefd[0]);
                oldfds.push_back(pipefd[1]);
                std::string SlaveName = "Slave No." + std::to_string(i);
                _process_pool.push_back(channel(pipefd[1], id, getpid(), SlaveName));
            }
        }
    }
};

ProcessPool* ProcessPool::_singletonProcessPool = nullptr;